
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

/**
 * AUTO-GENERATED FILE. DO NOT MODIFY.
 */

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
function dataIndexMapValueLength(valNumOrArrLengthMoreThan2) {
	return valNumOrArrLengthMoreThan2 == null ? 0 : valNumOrArrLengthMoreThan2.length || 1;
}

function defaultKeyGetter(item) {
	return item;
}

var DataDiffer =
/** @class */
function () {
	/**
   * @param context Can be visited by this.context in callback.
   */
	function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context, // By default: 'oneToOne'.
		diffMode) {
		this._old = oldArr;
		this._new = newArr;
		this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;
		this._newKeyGetter = newKeyGetter || defaultKeyGetter; // Visible in callback via `this.context`;

		this.context = context;
		this._diffModeMultiple = diffMode === 'multiple';
	}
	/**
   * Callback function when add a data
   */

	DataDiffer.prototype.add = function (func) {
		this._add = func;
		return this;
	};
	/**
   * Callback function when update a data
   */

	DataDiffer.prototype.update = function (func) {
		this._update = func;
		return this;
	};
	/**
   * Callback function when update a data and only work in `cbMode: 'byKey'`.
   */

	DataDiffer.prototype.updateManyToOne = function (func) {
		this._updateManyToOne = func;
		return this;
	};
	/**
   * Callback function when update a data and only work in `cbMode: 'byKey'`.
   */

	DataDiffer.prototype.updateOneToMany = function (func) {
		this._updateOneToMany = func;
		return this;
	};
	/**
   * Callback function when update a data and only work in `cbMode: 'byKey'`.
   */

	DataDiffer.prototype.updateManyToMany = function (func) {
		this._updateManyToMany = func;
		return this;
	};
	/**
   * Callback function when remove a data
   */

	DataDiffer.prototype.remove = function (func) {
		this._remove = func;
		return this;
	};

	DataDiffer.prototype.execute = function () {
		this[this._diffModeMultiple ? '_executeMultiple' : '_executeOneToOne']();
	};

	DataDiffer.prototype._executeOneToOne = function () {
		var oldArr = this._old;
		var newArr = this._new;
		var newDataIndexMap = {};
		var oldDataKeyArr = new Array(oldArr.length);
		var newDataKeyArr = new Array(newArr.length);

		this._initIndexMap(oldArr, null, oldDataKeyArr, '_oldKeyGetter');

		this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter');

		for (var i = 0; i < oldArr.length; i++) {
			var oldKey = oldDataKeyArr[i];
			var newIdxMapVal = newDataIndexMap[oldKey];
			var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal); // idx can never be empty array here. see 'set null' logic below.

			if (newIdxMapValLen > 1) {
				// Consider there is duplicate key (for example, use dataItem.name as key).
				// We should make sure every item in newArr and oldArr can be visited.
				var newIdx = newIdxMapVal.shift();

				if (newIdxMapVal.length === 1) {
					newDataIndexMap[oldKey] = newIdxMapVal[0];
				}

				this._update && this._update(newIdx, i);
			} else if (newIdxMapValLen === 1) {
				newDataIndexMap[oldKey] = null;
				this._update && this._update(newIdxMapVal, i);
			} else {
				this._remove && this._remove(i);
			}
		}

		this._performRestAdd(newDataKeyArr, newDataIndexMap);
	};
	/**
   * For example, consider the case:
   * oldData: [o0, o1, o2, o3, o4, o5, o6, o7],
   * newData: [n0, n1, n2, n3, n4, n5, n6, n7, n8],
   * Where:
   *     o0, o1, n0 has key 'a' (many to one)
   *     o5, n4, n5, n6 has key 'b' (one to many)
   *     o2, n1 has key 'c' (one to one)
   *     n2, n3 has key 'd' (add)
   *     o3, o4 has key 'e' (remove)
   *     o6, o7, n7, n8 has key 'f' (many to many, treated as add and remove)
   * Then:
   *     (The order of the following directives are not ensured.)
   *     this._updateManyToOne(n0, [o0, o1]);
   *     this._updateOneToMany([n4, n5, n6], o5);
   *     this._update(n1, o2);
   *     this._remove(o3);
   *     this._remove(o4);
   *     this._remove(o6);
   *     this._remove(o7);
   *     this._add(n2);
   *     this._add(n3);
   *     this._add(n7);
   *     this._add(n8);
   */

	DataDiffer.prototype._executeMultiple = function () {
		var oldArr = this._old;
		var newArr = this._new;
		var oldDataIndexMap = {};
		var newDataIndexMap = {};
		var oldDataKeyArr = [];
		var newDataKeyArr = [];

		this._initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter');

		this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter');

		for (var i = 0; i < oldDataKeyArr.length; i++) {
			var oldKey = oldDataKeyArr[i];
			var oldIdxMapVal = oldDataIndexMap[oldKey];
			var newIdxMapVal = newDataIndexMap[oldKey];
			var oldIdxMapValLen = dataIndexMapValueLength(oldIdxMapVal);
			var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal);

			if (oldIdxMapValLen > 1 && newIdxMapValLen === 1) {
				this._updateManyToOne && this._updateManyToOne(newIdxMapVal, oldIdxMapVal);
				newDataIndexMap[oldKey] = null;
			} else if (oldIdxMapValLen === 1 && newIdxMapValLen > 1) {
				this._updateOneToMany && this._updateOneToMany(newIdxMapVal, oldIdxMapVal);
				newDataIndexMap[oldKey] = null;
			} else if (oldIdxMapValLen === 1 && newIdxMapValLen === 1) {
				this._update && this._update(newIdxMapVal, oldIdxMapVal);
				newDataIndexMap[oldKey] = null;
			} else if (oldIdxMapValLen > 1 && newIdxMapValLen > 1) {
				this._updateManyToMany && this._updateManyToMany(newIdxMapVal, oldIdxMapVal);
				newDataIndexMap[oldKey] = null;
			} else if (oldIdxMapValLen > 1) {
				for (var i_1 = 0; i_1 < oldIdxMapValLen; i_1++) {
					this._remove && this._remove(oldIdxMapVal[i_1]);
				}
			} else {
				this._remove && this._remove(oldIdxMapVal);
			}
		}

		this._performRestAdd(newDataKeyArr, newDataIndexMap);
	};

	DataDiffer.prototype._performRestAdd = function (newDataKeyArr, newDataIndexMap) {
		for (var i = 0; i < newDataKeyArr.length; i++) {
			var newKey = newDataKeyArr[i];
			var newIdxMapVal = newDataIndexMap[newKey];
			var idxMapValLen = dataIndexMapValueLength(newIdxMapVal);

			if (idxMapValLen > 1) {
				for (var j = 0; j < idxMapValLen; j++) {
					this._add && this._add(newIdxMapVal[j]);
				}
			} else if (idxMapValLen === 1) {
				this._add && this._add(newIdxMapVal);
			} // Support both `newDataKeyArr` are duplication removed or not removed.

			newDataIndexMap[newKey] = null;
		}
	};

	DataDiffer.prototype._initIndexMap = function (arr, // Can be null.
		map, // In 'byKey', the output `keyArr` is duplication removed.
		// In 'byIndex', the output `keyArr` is not duplication removed and
		//     its indices are accurately corresponding to `arr`.
		keyArr, keyGetterName) {
		var cbModeMultiple = this._diffModeMultiple;

		for (var i = 0; i < arr.length; i++) {
			// Add prefix to avoid conflict with Object.prototype.
			var key = '_ec_' + this[keyGetterName](arr[i], i);

			if (!cbModeMultiple) {
				keyArr[i] = key;
			}

			if (!map) {
				continue;
			}

			var idxMapVal = map[key];
			var idxMapValLen = dataIndexMapValueLength(idxMapVal);

			if (idxMapValLen === 0) {
				// Simple optimize: in most cases, one index has one key,
				// do not need array.
				map[key] = i;

				if (cbModeMultiple) {
					keyArr.push(key);
				}
			} else if (idxMapValLen === 1) {
				map[key] = [idxMapVal, i];
			} else {
				idxMapVal.push(i);
			}
		}
	};

	return DataDiffer;
}();

export default DataDiffer;